package example.core.controller;

import com.alibaba.fastjson.JSON;
import example.core.log.FcLog;
import example.core.springcontext.ApplicationContextUtil;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Controller 工具包。
 * @author 王振宇
 */
public class ControllerMateUtil {
    public static String LOG_TAG = "Controller:";

    /**
     * controller缓存。
     */
    static ConcurrentHashMap<String, ControllerMateData> controllerCashMap = new ConcurrentHashMap<>();

    /**
     * 初始化Url路由池。
     */
    public static void initCashMap() {
        FcLog log = ApplicationContextUtil.applicationContext.getBean(FcLog.class);
        Map<String, Object> restControllerMap = ApplicationContextUtil.applicationContext.getBeansWithAnnotation(RestController.class);
        for(Object bean : restControllerMap.values()) {
            Class cls = bean.getClass();

            // 获取所有public 方法 获取对应的 Post方法
            Method[] methods = cls.getMethods();
            for (Method method : methods) {

                // 存放url 地址
                StringBuffer stringBuffer = new StringBuffer();

                // 获取类 是否标记了注解
                RequestMapping requestMapping = (RequestMapping) cls.getAnnotation(RequestMapping.class);
                if (!ObjectUtils.isEmpty(requestMapping)) {
                    stringBuffer.append(requestMapping.value()[0]);
                }

                // 赋值要进行反射调用的对象
                ControllerMateData controllerMateData = new ControllerMateData();
                controllerMateData.setBean(bean);
                controllerMateData.setClassName(cls.getName());
                controllerMateData.setMethodName(method.getName());
                controllerMateData.setPackageUrl(cls.getPackage().getName());
                controllerMateData.setMethod(method);
                controllerMateData.setParameterTypes(method.getParameterTypes());

                // 获取对应的注解
                boolean isMapping = false;
                requestMapping = method.getAnnotation(RequestMapping.class);
                if (!ObjectUtils.isEmpty(requestMapping)) {
                    stringBuffer.append(requestMapping.value()[0]);
                    isMapping = true;
                }
                PostMapping postMapping = method.getAnnotation(PostMapping.class);
                if (!ObjectUtils.isEmpty(postMapping)) {
                    stringBuffer.append(postMapping.value()[0]);
                    controllerMateData.setHttpMethod("POST");
                    isMapping = true;
                }
                GetMapping getMapping = method.getAnnotation(GetMapping.class);
                if (!ObjectUtils.isEmpty(getMapping)) {
                    stringBuffer.append(getMapping.value()[0]);
                    isMapping = true;
                    controllerMateData.setHttpMethod("GET");
                }
                if (isMapping) {
                    log.info(stringBuffer.toString() + ": cache load");
                    controllerCashMap.put(stringBuffer.toString(), controllerMateData);
                }
            }
        }
    }

    /**
     * 获取对应的url Controller。
     * @param key url
     * @return 需要调用的Controller
     */
    public static ControllerMateData getMapper(String key) {
        return controllerCashMap.get(key);
    }

    /**
     * 调用方法。
     * @param controllerMateData Controller
     * @param jsonBody 请求报文
     * @return Controller 返回
     */
    public static Object runMethod(ControllerMateData controllerMateData, String jsonBody) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Object result = null;
        Object bean = controllerMateData.getBean();
        Method method = controllerMateData.getMethod();
        if (controllerMateData.getParameterTypes().length > 0) {
            Class<?> parameterType = controllerMateData.getParameterTypes()[0];
            Object paramBean = JSON.parseObject(jsonBody, parameterType);
            result = method.invoke(bean, paramBean);
        } else {
            result = method.invoke(bean);
        }
        return result;
    }
}
